/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.editpart;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.util.UMLUtil;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.figures.IconFigure;
import com.cea.papyrus.core.figures.NodeFigure;
import com.cea.papyrus.core.figures.NoteFigure;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.listener.UmlElementListener;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * 
 * 
 * @author Chokri Mraidha
 * 
 * This class handles stereotype property values display in diagrams.
 */
public class StereotypePropertiesDisplayEditPart extends GraphNodeEditPart {


	private static final String NO_VALUE_MESSAGE = "<no value> The profile has changed, you have to reapply it!";
	/**
	 * 
	 */
	protected UmlElementListener _adapter ;

	/**
	 * 
	 * 
	 * @param modelManager 
	 * @param graphNode 
	 */
	public StereotypePropertiesDisplayEditPart(GraphNode graphNode,ModelManager modelManager){
		super(graphNode, modelManager);
		_adapter=new UmlElementListener(this);
	}

	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	protected IFigure createFigure() {
		return new NoteFigure();			
	}
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.classdiagram.editpart.CommentEditPart#refreshVisuals()
	 */
	/**
	 * 
	 */
	@Override
	protected void refreshVisuals() {
		Debug.debug(this, "refreshvisuals", channel);

		Dimension size = getGraphNode().getSize();
		Point p = getGraphNode().getPosition();
		Rectangle r = new Rectangle(p ,size);

		IFigure fig = getFigure();

		// if fig is a stereotype figure
		if (!(fig instanceof NoteFigure)) {
			IconFigure icf = (IconFigure) getFigure();
			//icf.setName(getUmlElement().getBody());
		} else {
			//org.eclipse.uml2.uml.Comment currentComment = (org.eclipse.uml2.uml.Comment) getUmlElement();
			//com.cea.utils.Element currentElement = new com.cea.utils.Element(getUmlElement());

			// Retrieve current class and figure
			NoteFigure f = (NoteFigure) fig; //get Class figure
			f.setComment(setPropertiesValues()); //set the content of the figure

			// if fig is a ClassFigure
			((NodeFigure) fig).refreshColors(getGraphNode());
			// set additionnal graphical parameter
			f.setfigureFont(getGraphNode().getFontFamily());
			f.setFontSize((getGraphNode().getFontSize()));
			f.setLineThickness((getGraphNode().getLineThickness()));

			//((NodeFigure) fig).refreshPresentation(getPresentation(), (Element) currentComment);
			//boolean hasIcon  = currentElement.hasIcon();
			//boolean hasShape = currentElement.hasShape();
			//f.refreshStereotypes(currentElement.getStereotypes(), getPresentation(), hasIcon, hasShape);
		}

		((GraphicalEditPart) getParent()).setLayoutConstraint(
				this,
				fig,
				r);
	}

	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.classdiagram.editpart.CommentEditPart#getUmlElement()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	public org.eclipse.uml2.uml.Element getUmlElement() {
		if(getGraphNode().getSemanticParent()!=null &&
				((GraphElement)getGraphNode().getSemanticParent()).getSemanticModel()!=null){
			return ((Uml1SemanticModelBridge)((GraphElement)getGraphNode().getSemanticParent()).getSemanticModel()).getElement();}
		return null;	
	}

	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.common.editpart.GraphNodeEditPart#activate()
	 */
	/**
	 * 
	 */
	@Override
	public void activate()
	{
		if (isActive()) {
			return;
		}


		/*if(getGraphNode().getSemanticParent()!=null){
			((Notifier) getGraphNode().getSemanticParent()).eAdapters().add(this);
		}*/
		if (getUmlElement()!=null){
			((Notifier)getUmlElement()).eAdapters().add(_adapter);
			if (!getUmlElement().getAppliedStereotypes().isEmpty()) {
				Iterator<Stereotype> it=getUmlElement().getAppliedStereotypes().iterator();
				while(it.hasNext()){
					Stereotype currentStereotype =it.next();
					((Notifier)currentStereotype).eAdapters().add(_adapter);
					Iterator<org.eclipse.uml2.uml.Property> itProperty =currentStereotype.getAllAttributes().iterator();
					while(itProperty.hasNext()){
						org.eclipse.uml2.uml.Property currentProperty=itProperty.next();
						((Notifier)currentProperty).eAdapters().add(_adapter);

					}
				}
			}
		}
		if (getGraphNode().getSemanticModel()!=null){
			((Notifier) getGraphNode().getSemanticModel()).eAdapters().add(_adapter);}


		super.activate();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#deactivate()
	 */
	/**
	 * 
	 */
	@Override
	public void deactivate()
	{
		if (!isActive()) {
			return;
		}

		// stop listening for changes in the model
		/*if(getGraphNode().getSemanticParent()!=null){
			((Notifier) getGraphNode().getSemanticParent()).eAdapters().remove(this);
		}*/
		if (getUmlElement()!=null) {
			((Notifier)getUmlElement()).eAdapters().remove(_adapter);
			if (!getUmlElement().getAppliedStereotypes().isEmpty()) {
				Iterator<Stereotype> it=getUmlElement().getAppliedStereotypes().iterator();
				while(it.hasNext()){
					Stereotype currentStereotype =it.next();
					((Notifier)currentStereotype).eAdapters().remove(_adapter);
					Iterator<org.eclipse.uml2.uml.Property> itProperty =currentStereotype.getAllAttributes().iterator();
					while(itProperty.hasNext()){
						org.eclipse.uml2.uml.Property currentProperty=itProperty.next();
						((Notifier)currentProperty).eAdapters().remove(_adapter);
					}
				}
			}
		}
		if (getGraphNode().getSemanticModel()!=null){
			((Notifier) getGraphNode().getSemanticModel()).eAdapters().remove(_adapter);}
		//if this is a icon figure we have to dispose associated image


		super.deactivate();
	}


	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.classdiagram.editpart.CommentEditPart#initLabelControlerManager()
	 */
	/**
	 * 
	 */
	protected void initLabelControlerManager() {
	}


	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.classdiagram.editpart.CommentEditPart#performDirectEdit(org.eclipse.gef.Request)
	 */
	/**
	 * 
	 * 
	 * @param req 
	 */
	public void performDirectEdit(Request req) {
		// no direct editing is allowed here
	}


	/**
	 * 
	 * 
	 * @return 
	 */
	private String setPropertiesValues() {
		String propValues="";

		//get list of properties to display
		if(getGraphNode().getSemanticParent()!=null){
			EList graphElementPropList = getGraphNode().getSemanticParent().getProperty();
			ArrayList<String> displayPropList = new ArrayList<String>();
			Iterator<Property> gePropIter = graphElementPropList.iterator();
			while(gePropIter.hasNext()) {
				Property prop = gePropIter.next();
				if(prop.getKey().equals(ModelerHelper.PROPERTY_STEREOTYPE_PROPERTY_VALUES_DISPLAY)) {
					displayPropList.add(prop.getValue());
				}
			}

			HashSet<Stereotype> stereoSet = new HashSet<Stereotype>();
			ArrayList<String> stPropList = new ArrayList<String>();
			Iterator<String> iter = displayPropList.iterator();
			while(iter.hasNext()){
				String currentProp = iter.next();
				stereoSet.add(getUmlElement().getAppliedStereotype((currentProp.substring(0, currentProp.lastIndexOf("::")))));
				stPropList.add(currentProp);
			}

			Iterator<Stereotype> stereoIter = stereoSet.iterator();
			while(stereoIter.hasNext()){
				Stereotype currentSt = stereoIter.next();
				if(currentSt!=null){
					propValues = propValues+" "+PapyrusPlugin.ST_LEFT+currentSt.getName()+PapyrusPlugin.ST_RIGHT+"\n";

					Iterator<String> stPropIter = getStereoPropertiesToDisplay(currentSt, stPropList).iterator();
					while(stPropIter.hasNext()){
						String stProp = stPropIter.next();
						org.eclipse.uml2.uml.Property currentProp=null;
						Iterator iterPro = currentSt.getAllAttributes().iterator();
						while(iterPro.hasNext()){
							org.eclipse.uml2.uml.Property tmpProperty=(org.eclipse.uml2.uml.Property) iterPro.next();
							if(tmpProperty.getName().equals(stProp)){
								currentProp=tmpProperty;
							}
						}
						if(currentProp==null){
							return NO_VALUE_MESSAGE;
						}


						Type propType = currentProp.getType();

						// property is an enumeration

						if(propType instanceof Enumeration) {
							if((currentProp.getUpper()==1) && (getUmlElement().getValue(currentSt, stProp)!=null)) {
								if((currentProp.getLower() != 0) || getUmlElement().getValue(currentSt, stProp)!=null) {
									if(currentProp.isSetDefault() || getUmlElement().getValue(currentSt, stProp)!=null) {
										propValues = propValues + "     "+stProp + " = " + ((EnumerationLiteral)getUmlElement().getValue(currentSt, stProp)).getLabel()+ "\n";;
									} else {
										propValues = propValues + "     "+stProp +"\n";
									}
								} else {
									propValues = propValues + "     "+stProp +"\n";
								}
							}

							// multiplicity is greater than one
							else {
								propValues = propValues + "     "+stProp + " = " +getUmlElement().getValue(currentSt, stProp)+ "\n";
							}
						}

						// property is a metaclass
						else if ((propType instanceof org.eclipse.uml2.uml.Class) &&
								(propType.getAppliedStereotypes() != null) &&
								(propType.getAppliedStereotypes().size() > 0) &&
								((Stereotype)propType.getAppliedStereotypes().get(0)).getName().equals("Metaclass")) {

							if((currentProp.getUpper()==1) && (getUmlElement().getValue(currentSt, stProp)!=null)) {
								propValues = propValues + "     "+stProp + " = " +((NamedElement)(getUmlElement().getValue(currentSt, stProp))).getQualifiedName()+"\n";
							}

							// multiplicity greater than one
							else if(currentProp.getUpper() != 1) {
								List values = (List)getUmlElement().getValue(currentSt, stProp);
								ArrayList elementNames = new ArrayList();
								if (values != null) {
									for(int count=0; count<values.size(); count++) {
										elementNames.add(((NamedElement)values.get(count)).getQualifiedName());
									}
								}
								propValues = propValues + "     "+stProp + " = " +elementNames+"\n";
							}

							// multiplicity = 1 and property value null
							else {
								propValues = propValues + "     "+stProp + " = " +getUmlElement().getValue(currentSt, stProp)+"\n";
							}

						}

						// property is a stereotype
						else if (propType instanceof Stereotype) {
							if((currentProp.getUpper()==1) && (getUmlElement().getValue(currentSt, stProp)!=null)) {

								// retrieve the base element from the stereotype application
								Object value = getUmlElement().getValue(currentSt, stProp);
								Element baseElement = UMLUtil.getBaseElement((EObject)value);

								// display the base element's qualified name
								propValues = propValues + "     "+stProp + " = " +((NamedElement)baseElement).getQualifiedName()+"\n";
							}

							// multiplicity greater than one
							else if(currentProp.getUpper() != 1){
								// retrieve the base element from the stereotype application
								List values = (List)getUmlElement().getValue(currentSt, stProp);
								ArrayList baseElements = new ArrayList();
								if(values!=null) {
									for(int k=0; k<values.size(); k++) {
										baseElements.add(((NamedElement)UMLUtil.getBaseElement((EObject)values.get(k))).getQualifiedName());
									}
								}

								propValues = propValues + "     "+stProp + " = " +baseElements+"\n";
							}

							// multiplicity = 1 and property value null
							else {
								propValues = propValues + "     "+stProp + " = "+(getUmlElement().getValue(currentSt, stProp))+"\n";
							}
						}

						// property is a composite class
						else if((propType instanceof org.eclipse.uml2.uml.Class) && !(propType instanceof Stereotype) && currentProp.isComposite()){
							propValues = propValues + "     "+stProp + " = "+currentProp.getName()+"\n";
						}

						// otherwise
						else  {
							if((currentProp.getLower() != 0) || getUmlElement().getValue(currentSt, stProp)!=null) {
								if(currentProp.isSetDefault() || getUmlElement().getValue(currentSt, currentProp.getName())!=null) {
									propValues = propValues + "     "+stProp + " = " +getUmlElement().getValue(currentSt, stProp)+"\n";
								} else {
									propValues = propValues + "     "+stProp+"\n";
								}
							} else {
								propValues = propValues + "     "+stProp+"\n";
							}
						}
					}

					// property type is a primitive type
					//else if(propType instanceof PrimitiveType) {
					//propValues = propValues + "     "+stProp + " = " + getUmlElement().getValue(currentSt, stProp) + "\n";
					//}

				}
			}
		}

		return propValues;
	}

	/**
	 * Returns the list of names (not qualified) of properties to display.
	 * 
	 * @param stereotype 
	 * @param stPropList 
	 * 
	 * @return 
	 */
	private ArrayList<String> getStereoPropertiesToDisplay(Stereotype stereotype, ArrayList<String> stPropList){
		ArrayList<String> result = new ArrayList<String>();

		Iterator<String> propIter = stPropList.iterator();
		while(propIter.hasNext()){
			String currentProp = propIter.next();
			if(currentProp.substring(0, currentProp.lastIndexOf("::")).equals(stereotype.getQualifiedName())) {
				result.add(currentProp.substring(currentProp.lastIndexOf("::")+2, currentProp.length()));
			}
		}
		return result;
	}

}
